/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_components/zhiqim_manager.htm
 *
 * Zhiqim Manager is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.manager.dao;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.zhiqim.httpd.HttpRequest;
import org.zhiqim.kernel.annotation.AnAlias;
import org.zhiqim.kernel.json.Jsons;
import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.kernel.util.Lists;
import org.zhiqim.kernel.util.Sqls;
import org.zhiqim.kernel.util.Validates;
import org.zhiqim.manager.ZmrBootstrap;
import org.zhiqim.manager.ZmrPassworder;
import org.zhiqim.manager.ZmrSessionUser;
import org.zhiqim.manager.dbo.ZmrDeptRule;
import org.zhiqim.manager.dbo.ZmrMenu;
import org.zhiqim.manager.dbo.ZmrOperator;
import org.zhiqim.manager.dbo.ZmrOperatorLog;
import org.zhiqim.manager.dbo.ZmrOperatorRule;
import org.zhiqim.manager.dbo.ZmrRoleRule;
import org.zhiqim.orm.ORM;
import org.zhiqim.orm.dbo.Selector;
import org.zhiqim.orm.dbo.Updater;

/**
 * 操作员数据访问对象
 *
 * @version v1.0.0 @author zouzhigang 2017-8-12 新建与整理
 */
@AnAlias("ZmrOperatorDao")
public class ZmrOperatorDao
{
    /*********************************************************************************************/
    //操作员基础信息
    /*********************************************************************************************/
    
    /**
     * 验证操作员密码
     * 
     * @param request       请求对象
     * @param operator      操作员对象
     * @param operatorPass  操作员密码
     * @return              =true表示成功
     * @throws Exception    可能的异常
     */
    public static boolean validatePassword(ZmrOperator operator, String operatorPass)
    {
        Asserts.as(operator!=null?null:"操作员对象不能为null");
        Asserts.as(!Validates.isEmptyBlank(operatorPass)?null:"操作员密码不能为空白");
        
        ZmrPassworder passworder = ZmrBootstrap.getPassworder();
        String encodePass = passworder.encode(operator.getOperatorCode(), operatorPass, operator.getOperatorPassSalt());
        return encodePass.equals(operator.getOperatorPass());
    }
    
    /**
     * 验证操作员密码
     * 
     * @param request       请求对象
     * @param operatorCode  操作员账号
     * @param operatorPass  操作员密码
     * @return              =true表示成功
     * @throws Exception    可能的异常
     */
    public static boolean validatePassword(String operatorCode, String operatorPass) throws Exception
    {
        Asserts.as(!Validates.isEmptyBlank(operatorCode)?null:"操作员账号不能为空白");
        Asserts.as(!Validates.isEmptyBlank( operatorPass)?null:"操作员密码不能为空白");
        
        ZmrOperator operator = ORM.table().item(ZmrOperator.class, operatorCode);
        if (operator == null)
            return false;
        
        return validatePassword(operator, operatorPass);
    }
    
    /*********************************************************************************************/
    //操作员参数相关
    /*********************************************************************************************/
    
    /**
     * 获取操作员参数
     * 
     * @param operatorCode  操作员账号
     * @param key           参数字段
     * @throws Exception    可能的异常
     */
    public static String getOperatorParam(String operatorCode, String key) throws Exception
    {
        Asserts.as(!Validates.isEmptyBlank(operatorCode)?null:"操作员账号不能为空白");
        
        ZmrOperator operator = ORM.table().item(ZmrOperator.class, operatorCode);
        if (operator == null)
            return null;
        
        return Jsons.getString(operator.getOperatorParam(), key);
    }
    
    /**
     * 获取操作员参数
     * 
     * @param request       请求对象
     * @param operator      操作员对象
     * @param key           参数字段
     * @throws Exception    可能的异常
     */
    public static String getOperatorParam(ZmrOperator operator, String key) throws Exception
    {
        Asserts.as(operator != null?null:"操作员账号不能为空白");
        
        return Jsons.getString(operator.getOperatorParam(), key);
    }
    
    /**
     * 更新操作员参数
     * 
     * @param request       请求对象
     * @param operatorCode  操作员账号
     * @param key           参数字段
     * @param value         参数值
     * @throws Exception    可能的异常
     */
    public static void addOrUpdateOperatorParam(String operatorCode, String key, Object value) throws Exception
    {
        ZmrOperator operator = ORM.table().item(ZmrOperator.class, operatorCode);
        if (operator == null)
            return;
        
        String operatorParam = operator.getOperatorParam();
        if (Validates.isEmptyBlank(operatorParam))
            operatorParam = "{}";
        operatorParam = Jsons.toStringAddOrUpdate(operatorParam, key, value);
        
        Updater updater = new Updater();
        updater.addField("operatorParam", operatorParam);
        updater.addMust("operatorCode", operatorCode);
        ORM.table().update(ZmrOperator.class, updater);
    }
    
    /**
     * 删除操作员参数
     * 
     * @param request       请求对象
     * @param operatorCode  操作员账号
     * @param key           参数字段
     * @throws Exception    可能的异常
     */
    public static void deleteOperatorParam(String operatorCode, String key) throws Exception
    {
        ZmrOperator operator = ORM.table().item(ZmrOperator.class, operatorCode);
        if (operator == null)
            return;
        
        String operatorParam = operator.getOperatorParam();
        if (Validates.isEmptyBlank(operatorParam))
            operatorParam = "{}";
        operatorParam = Jsons.toStringRemove(operatorParam, key);
        
        Updater updater = new Updater();
        updater.addField("operatorParam", operatorParam);
        updater.addMust("operatorCode", operatorCode);
        ORM.table().update(ZmrOperator.class, updater);
    }
    
    /*********************************************************************************************/
    //操作员权限相关
    /*********************************************************************************************/
    
    /**
     * 获取所有显示的菜单列表
     * 
     * @return              全部菜单列表
     * @throws Exception    可能的异常
     */
    public static List<ZmrMenu> getValidMenuList() throws Exception
    {
        return ORM.table().list(ZmrMenu.class, new Selector("menuStatus", 0).addOrderbyAsc("menuLevel, menuCode"));
    }
    
    /**
     * 获取操作员菜单列表
     * 
     * @param request       请求对象
     * @param operatorCode  操作员账号
     * @return              菜单列表
     * @throws Exception    可能的异常
     */
    public static List<ZmrMenu> getOperatorMenuList(String operatorCode) throws Exception
    {
        Asserts.assertNotEmpty(operatorCode, "操作员账号不允许为空");
        
        ZmrOperator operator = ORM.table().item(ZmrOperator.class, operatorCode);
        if (operator == null)
            return new ArrayList<ZmrMenu>();
        
        return getOperatorMenuList(operator);
    }
    
    /**
     * 获取操作员菜单列表
     * 
     * @param request       请求对象
     * @param operator      操作员
     * @return              菜单列表
     * @throws Exception    可能的异常
     */
    public static List<ZmrMenu> getOperatorMenuList(ZmrOperator operator) throws Exception
    {
        if (operator.getOperatorType() == 0)
        {//1.超级管理员（加载隐藏的用于测试，但不加载关闭的，需要加载关闭的请在菜单管理中修改为显示或隐藏）
            return ORM.table().list(ZmrMenu.class, new Selector().addMustThenLE("menuStatus", 1).addOrderbyAsc("menuLevel, menuCode"));
        }
        
        List<ZmrMenu> menuList = getValidMenuList();
        if (operator.getOperatorType() == 1)
        {//2.管理员（加载所有显示的，隐藏的管理员可以在菜单管理中修改为显示，关闭的只有超级管理员可以修改，该功能是程序级的或不完善的功能，非程序员和运维操作容易出错）
            return menuList;
        }
        
        //3.操作员（独立权限|部门权限|角色权限的合集）
        List<ZmrOperatorRule> operatorRuleList = ORM.table().list(ZmrOperatorRule.class, new Selector("operatorCode", operator.getOperatorCode()));
        
        List<ZmrDeptRule> deptRuleList = new ArrayList<>();
        String deptIds = ZmrParamDao.isOperatorDeptAllRule()?operator.getOperatorDeptAll():operator.getOperatorDept();
        List<Long> deptIdList = Lists.toLongList(deptIds);
        for (long deptId : deptIdList)
        {
            List<ZmrDeptRule> ruleList = ORM.table().list(ZmrDeptRule.class, new Selector("deptId", deptId));
            deptRuleList.addAll(ruleList);
        }
        
        List<ZmrRoleRule> roleRuleList = new ArrayList<>();
        List<Long> roleIdList = Lists.toLongList(operator.getOperatorRole());
        for (long roleId : roleIdList)
        {
            List<ZmrRoleRule> ruleList = ORM.table().list(ZmrRoleRule.class, new Selector("roleId", roleId));
            roleRuleList.addAll(ruleList);
        }
        
        //属于三者之一则认为有效
        for (Iterator<ZmrMenu> it=menuList.iterator();it.hasNext();)
        {
            String menuCode = it.next().getMenuCode();
            if (isOperatorRule(operatorRuleList, menuCode))
                continue;
            
            if (isDeptRule(deptRuleList, menuCode))
                continue;
            
            if (isRoleRule(roleRuleList, menuCode))
                continue;

            it.remove();
        }

        return menuList;
    }
    
    /** 是否操作员独立权限（独立权限设置页面用到） */
    public static boolean isOperatorRule(List<ZmrOperatorRule> ruleList, String menuCode)
    {
        for (ZmrOperatorRule rule : ruleList)
        {
            if (menuCode.equals(rule.getMenuCode()))
                return true;
        }
        
        return false;
    }
    
    /** 是否部门权限（部门权限设置页面用到） */
    public static boolean isDeptRule(List<ZmrDeptRule> ruleList, String menuCode)
    {
        for (ZmrDeptRule rule : ruleList)
        {
            if (menuCode.equals(rule.getMenuCode()))
                return true;
        }
        
        return false;
    }
    
    /** 是否角色权限（角色权限设置页面用到） */
    public static boolean isRoleRule(List<ZmrRoleRule> ruleList, String menuCode)
    {
        for (ZmrRoleRule rule : ruleList)
        {
            if (menuCode.equals(rule.getMenuCode()))
                return true;
        }
        
        return false;
    }
    
    /*********************************************************************************************/
    //操作日志相关
    /*********************************************************************************************/
    
    /**
     * 增加操作日志，从会话中读取操作员账号和IP，操作描述为空
     * 
     * @param request        请求对象
     * @param operateFeature 操作功能
     * @throws Exception     可能的异常
     */
    public static void addOperateLog(HttpRequest request, String operateFeature) throws Exception
    {
        addOperateLog(request, operateFeature, null);
    }
    
    /**
     * 增加操作日志，从会话中读取操作员账号和IP
     * 
     * @param request        请求对象
     * @param operateFeature 操作功能
     * @param operateDesc    操作描述
     * @throws Exception     可能的异常
     */
    public static void addOperateLog(HttpRequest request, String operateFeature, String operateDesc) throws Exception
    {
        ZmrSessionUser sessionUser = request.getSessionUser(ZmrSessionUser.class);
        if (sessionUser == null)
            return;
        
        String operatorCode = sessionUser.getOperatorCode();
        String operateIp = request.getRemoteAddr();
        addOperateLog(operatorCode, operateIp, operateFeature, operateDesc);
    }
    
    /**
     * 增加操作日志，四个参数都设置进来
     * 
     * @param operatorCode   操作员账号
     * @param operateIp      操作IP
     * @param operateFeature 操作功能
     * @param operateTime    操作时间
     * @param operateDesc    操作描述
     * @throws Exception     可能的异常
     */
    public static void addOperateLog(String operatorCode, String operateIp, String operateFeature, String operateDesc) throws Exception
    {
        ZmrOperatorLog log = new ZmrOperatorLog();
        log.setOperatorCode(operatorCode);
        log.setOperateTime(Sqls.nowTimestamp());
        log.setOperateIp(operateIp);
        log.setOperateFeature(operateFeature);
        log.setOperateDesc(operateDesc);
        
        ORM.table().insert(log);
    }
}
